//----------------------------------------------------------------------------
//  ,-.
// /_\___ ,   Ninja Framework
// '  ___\    (C) Riccardo Capra - http://riccar.do
//  //   \\
//----------------------------------------------------------------------------
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------

#include "NjExample01.h"
#include "NjDummySyncObjectTest.h"
#include "NjSynchronizationTest.h"
#include "NjSharedSyncObjectTest.h"
#include "NjDebugSystemTestImplementation.h"
#include "NjSharedSyncObjectConstants.h"
#include "NjPlatformSyncObject.h"
#include "NjThreadsSystem.h"
#include "NjPlacementNew.h"
#include "NjTestUnit.h"
#include "NjSynchronizationTest.h"
#include "NjRandUint.h"
#include "NjExampleRandGenerator.h"
#include "NjMemUtils.h"

//----------------------------------------------------------------------------
class NjDummySyncObjectTest::Static
{
public:
	typedef NjSharedSyncObjectConstants::Query<NjDummySyncObject> Constants;

	static void RunDummySyncObjectAssert();
	static void RunDummySyncObjectAssertThread(void * pData);
	static void RunDummySyncObjectPerformances();

	NJ_DISABLE_INSTANCE(Static);
};
//----------------------------------------------------------------------------
void NjDummySyncObjectTest::Run(void)
{
	NjSharedSyncObjectTest<NjDummySyncObject>::RunFunctional();
	Static::RunDummySyncObjectPerformances();
	Static::RunDummySyncObjectAssert();
	NjSharedSyncObjectTest<NjDummySyncObject>::RunLocker();
}
//----------------------------------------------------------------------------
void NjDummySyncObjectTest::Static::RunDummySyncObjectAssert()
{
#if NJ_DUMMY_SYNC_OBJECT_STORE_LOCKER_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED
	NJ_TEST_UNITS_GROUP(NjSharedSyncObjectConstants::Query<NjDummySyncObject>::ms_szSyncObject);
		NJ_TEST_UNIT("Assertion (concurrent lock, destruction)");
			NjSmartPtr<NjDebugSystem::Implementation> spImplementation(NjDebugSystem::GetImplementation());
			NjDebugSystemTestImplementation * const pTestBreakpoint(NjNew(NjDebugSystemTestImplementation(spImplementation)));
			NJ_TEST(pTestBreakpoint!=NJ_NULL);
			NjDebugSystem::SetImplementation(pTestBreakpoint);

			{
				NjDummySyncObject kSyncObject;
				kSyncObject.Lock();
				NJ_TEST(!pTestBreakpoint->WasTriggeredBreakpoint());

				NjThreadsSystem::Thread * const pThread(NjThreadsSystem::CreateThread(RunDummySyncObjectAssertThread, &kSyncObject));

				NJ_TEST(pThread!=NJ_NULL);
				NJ_TEST(NjThreadsSystem::JoinThread(pThread));
				NJ_TEST(NjThreadsSystem::DestroyThread(pThread));
				NJ_TEST(pTestBreakpoint->WasTriggeredBreakpoint());
			}

			pTestBreakpoint->WasTriggeredBreakpoint();

			{
				NjDummySyncObject kSyncObject;

				NjThreadsSystem::Thread * const pThread(NjThreadsSystem::CreateThread(RunDummySyncObjectAssertThread, &kSyncObject));

				NJ_TEST(pThread!=NJ_NULL);
				NJ_TEST(NjThreadsSystem::JoinThread(pThread));
				NJ_TEST(NjThreadsSystem::DestroyThread(pThread));
				NJ_TEST(!pTestBreakpoint->WasTriggeredBreakpoint());
			}

			NJ_TEST(pTestBreakpoint->WasTriggeredBreakpoint());

			NjDebugSystem::SetImplementation(spImplementation);
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
#endif // NJ_DUMMY_SYNC_OBJECT_STORE_LOCKER_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED
}
//----------------------------------------------------------------------------
void NjDummySyncObjectTest::Static::RunDummySyncObjectAssertThread(void * pData)
{
	NjDummySyncObject * const pSyncObject(reinterpret_cast<NjDummySyncObject *>(pData));
	pSyncObject->Lock();
}
//----------------------------------------------------------------------------
void NjDummySyncObjectTest::Static::RunDummySyncObjectPerformances()
{
	NjSharedSyncObjectTest<NjDummySyncObject>::RunPerformancesConstructor();

	NJ_TEST_UNITS_GROUP(Static::Constants::ms_szSyncObject);
		NJ_TEST_UNIT("Lock, TryLock, Unlock (performances)");
			static const NjUint OPERATIONS = 4000000*NjSharedSyncObjectConstants::ITERATIONS_FACTOR;
			static const NjUint ITERATIONS = 200*NjSharedSyncObjectConstants::ITERATIONS_FACTOR;
			static NjBool s_bResult=NJ_FALSE;

			NjUint * const pOperations(NjNewArray(NjUint[ITERATIONS]));

			NjUint uTotOperations(OPERATIONS);
			NjRandUint<EXAMPLE_RAND_GENERATOR> kRand(0, 21);
			NjUint uIteration(ITERATIONS);
			NjUint uMaxOperations(0);
			while (uIteration>0)
			{
				NjUint uOperations(uTotOperations/uIteration);
				--uIteration;

				if (uIteration>0)
					uOperations+=(NjInt(uOperations)*NjInt(kRand()-10))/100;

				if (uOperations>uMaxOperations)
					uMaxOperations=uOperations;

				pOperations[uIteration]=uOperations;
				uTotOperations-=uOperations;
			}

			NjUint8 * const pBuffer(NjNewArray(NjUint8[(sizeof(NjDummySyncObject)+sizeof(NjPlatformSyncObject::SyncObject))*uMaxOperations]));
			NjDummySyncObject * const pDummySyncObjects(reinterpret_cast<NjDummySyncObject *>(pBuffer));
			NjPlatformSyncObject::SyncObject * const pPlatformSyncObjects(reinterpret_cast<NjPlatformSyncObject::SyncObject *>(pDummySyncObjects+uMaxOperations));

			{
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uMaxOperations);
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Initialize(*pPlatformPos);
					++pPlatformPos;
				}

				NjDummySyncObject * pImplPos(pDummySyncObjects);
				NjDummySyncObject const * const pImplEnd(pDummySyncObjects+uMaxOperations);
				while (pImplPos<pImplEnd)
				{
					NjPlacementNew kPlacement(pImplPos);
					NJ_PLACEMENT_NEW(kPlacement, NjDummySyncObject);

					++pImplPos;
				}
			}

			NjHighResolutionTimer kPlatformSyncObjectLock;
			NjHighResolutionTimer kPlatformSyncObjectUnlock;
			NjHighResolutionTimer kPlatformSyncObjectTryLockSuccess;
			NjHighResolutionTimer kSyncObjectLock;
			NjHighResolutionTimer kSyncObjectUnlock;
			NjHighResolutionTimer kSyncObjectTryLockSuccess;
			NjHighResolutionTimer kOverHead;

			NjUint const * pPos(pOperations);
			NjUint const * const pEnd(pOperations+ITERATIONS);

			while (pPos!=pEnd)
			{
				NjUint const uOperations(*pPos);
				NjDummySyncObject * pImplPos(pDummySyncObjects);
				NjDummySyncObject const * const pImplEnd(pImplPos+uOperations);
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uOperations);

				kOverHead.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					++pPlatformPos;
				}
				kOverHead.Pause();

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectLock.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Lock(*pPlatformPos);
					++pPlatformPos;
				}
				kPlatformSyncObjectLock.Pause();

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectUnlock.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Unlock(*pPlatformPos);
					++pPlatformPos;
				}
				kPlatformSyncObjectUnlock.Pause();

				pPlatformPos=pPlatformSyncObjects;
				kPlatformSyncObjectTryLockSuccess.Start();
				while (pPlatformPos<pPlatformEnd)
				{
					s_bResult=NjPlatformSyncObject::TryLock(*pPlatformPos, 0);
					++pPlatformPos;
				}
				kPlatformSyncObjectTryLockSuccess.Pause();
				
				NJ_TEST(s_bResult);

				pPlatformPos=pPlatformSyncObjects;
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Unlock(*pPlatformPos);
					++pPlatformPos;
				}

				kSyncObjectLock.Start();
				while (pImplPos<pImplEnd)
				{
					pImplPos->Lock();
					++pImplPos;
				}
				kSyncObjectLock.Pause();

				pImplPos=pDummySyncObjects;
				kSyncObjectUnlock.Start();
				while (pImplPos<pImplEnd)
				{
					pImplPos->Unlock();
					++pImplPos;
				}
				kSyncObjectUnlock.Pause();

				pImplPos=pDummySyncObjects;
				kSyncObjectTryLockSuccess.Start();
				while (pImplPos<pImplEnd)
				{
					s_bResult=pImplPos->TryLock(0);
					++pImplPos;
				}
				kSyncObjectTryLockSuccess.Pause();

				NJ_TEST(s_bResult);

				pImplPos=pDummySyncObjects;
				while (pImplPos<pImplEnd)
				{
					pImplPos->Unlock();
					++pImplPos;
				}
				++pPos;
			}

			{
				NjPlatformSyncObject::SyncObject * pPlatformPos(pPlatformSyncObjects);
				NjPlatformSyncObject::SyncObject const * const pPlatformEnd(pPlatformPos+uMaxOperations);
				while (pPlatformPos<pPlatformEnd)
				{
					NjPlatformSyncObject::Destroy(*pPlatformPos);
					++pPlatformPos;
				}

				NjDummySyncObject * pImplPos(pDummySyncObjects);
				NjDummySyncObject const * const pImplEnd(pImplPos+uMaxOperations);
				while (pImplPos<pImplEnd)
				{
					pImplPos->~NjDummySyncObject();
					++pImplPos;
				}
			}

			NjDeleteArray(pBuffer);
			NjDeleteArray(pOperations);

			NjFloat fOverHead(kOverHead.GetTimeS());
//			NjFloat fPlatformSyncObjectLock(kPlatformSyncObjectLock.GetTimeS()-fOverHead);
//			NjFloat fPlatformSyncObjectUnlock(kPlatformSyncObjectUnlock.GetTimeS()-fOverHead);
//			NjFloat fPlatformSyncObjectTryLockSuccess(kPlatformSyncObjectTryLockSuccess.GetTimeS()-fOverHead);
			NjFloat fSyncObjectLock(kSyncObjectLock.GetTimeS()-fOverHead);
			NjFloat fSyncObjectUnlock(kSyncObjectUnlock.GetTimeS()-fOverHead);
			NjFloat fSyncObjectTryLockSuccess(kSyncObjectTryLockSuccess.GetTimeS()-fOverHead);

//			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::METH_LOCK, OPERATIONS, fPlatformSyncObjectLock);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::METH_LOCK, OPERATIONS, fSyncObjectLock);
//			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::METH_UNLOCK, OPERATIONS, fPlatformSyncObjectUnlock);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::METH_UNLOCK, OPERATIONS, fSyncObjectUnlock);
//			NjSynchronizationStats::Store(NjSynchronizationStats::PLATFORM_SYNC_OBJECT, NjSynchronizationStats::METH_TRY_LOCK_SUCCESS, OPERATIONS, fPlatformSyncObjectTryLockSuccess);
			NjSynchronizationStats::Store(Static::Constants::ms_eCreationRow, NjSynchronizationStats::METH_TRY_LOCK_SUCCESS, OPERATIONS, fSyncObjectTryLockSuccess);
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
}
//----------------------------------------------------------------------------
